package org.jaronsource.framework.plugins.dictionary;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.annotation.PostConstruct;

import org.jaronsource.framework.core.dao.support.Criteria;
import org.jaronsource.framework.core.dao.support.Page;
import org.jaronsource.framework.plugins.dictionary.domain.Dictionary;
import org.jaronsource.framework.plugins.dictionary.service.DictionaryService;
import org.jaronsource.framework.util.BeanUtils;
import org.jaronsource.framework.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
@Lazy
public class DictionaryHelper {

	@Autowired
	private DictionaryService dictionaryService;

	private Map<String, Map<String, Dictionary>> dictionaries = new TreeMap<String, Map<String, Dictionary>>();

	public Map<String, Map<String, Dictionary>> getDictionaries() {
		return dictionaries;
	}

	private List<String> excludeFieldName = new ArrayList<String>();

	@PostConstruct
	public void init() {

		List<Dictionary> dicList = dictionaryService.find( new Criteria() );

		for ( Dictionary dict : dicList ) {
			String type = dict.getDictType();
			Map<String, Dictionary> tmpMap = dictionaries.get( type );
			if ( tmpMap == null ) {
				tmpMap = new HashMap<String, Dictionary>();
				dictionaries.put( type, tmpMap );
			}
			tmpMap.put( dict.getDictKey(), dict );
		}

		excludeFieldName.add( "_methods_" );
		excludeFieldName.add( "_filter_signature" );
		excludeFieldName.add( "serialVersionUID" );
		excludeFieldName.add( "handler" );
	}

	public void update() {
		clear();
		init();
	}

	public void clear() {
		dictionaries.clear();
	}

	public Object convert( Object bean, String type, String fieldName ) {

		String oldValue = null;
		String newValue = null;
		try {
			oldValue = BeanUtils.getProperty( bean, fieldName );
		}
		catch ( Exception e ) {}
		newValue = lookupDictValue0( type, oldValue );

		try {
			BeanUtils.copyProperty( bean, fieldName, newValue );
		}
		catch ( Exception e ) {}

		return bean;
	}

	public Dictionary lookupDict( String type, String key ) {
		if ( StringUtils.isBlank( type ) || StringUtils.isBlank( key ) ) return null;
		Map<String, Dictionary> typeMap = dictionaries.get( type );
		return typeMap == null ? null : typeMap.get( key );
	}

	/**
	 * 从缓存中获得指定type, 多个key的字典列表
	 * 
	 * @param type 字典类型
	 * @param key 逗号分隔的key串
	 * @return
	 */
	public Dictionary[] lookupDicts( String type, String csvKey ) {
		if ( StringUtils.isBlank( type ) || StringUtils.isBlank( csvKey ) ) return null;

		Map<String, Dictionary> typeMap = dictionaries.get( type );
		List<Dictionary> result = new ArrayList<Dictionary>();
		if ( typeMap != null ) {
			String[] temp = StringUtils.split( csvKey, "," );
			for ( int i = 0; i < temp.length; i++ ) {
				temp[i] = temp[i].trim();
				if ( typeMap.get( temp[i] ) != null ) result.add( typeMap.get( temp[i] ) );
			}
		}
		return result.toArray( new Dictionary[0] );
	}

	public String lookupDictValue0( String type, String key ) {
		Dictionary[] dicts = lookupDicts( type, key );
		if ( dicts == null ) return StringUtils.EMPTY;
		String[] result = new String[dicts.length];
		for ( int i = 0; i < dicts.length; i++ ) {
			result[i] = dicts[i].getDictValue0();
		}
		return StringUtils.join( result, "," );
	}

	public String lookupDictValue1( String type, String key ) {
		Dictionary[] dicts = lookupDicts( type, key );
		if ( dicts == null ) return StringUtils.EMPTY;
		String[] result = new String[dicts.length];
		for ( int i = 0; i < dicts.length; i++ ) {
			result[i] = dicts[i].getDictValue1();
		}
		return StringUtils.join( result, "," );
	}

	public String lookupDictValue2( String type, String key ) {
		Dictionary[] dicts = lookupDicts( type, key );
		if ( dicts == null ) return StringUtils.EMPTY;
		String[] result = new String[dicts.length];
		for ( int i = 0; i < dicts.length; i++ ) {
			result[i] = dicts[i].getDictValue2();
		}
		return StringUtils.join( result, "," );
	}

	public String lookupDictValue3( String type, String key ) {
		Dictionary[] dicts = lookupDicts( type, key );
		if ( dicts == null ) return StringUtils.EMPTY;
		String[] result = new String[dicts.length];
		for ( int i = 0; i < dicts.length; i++ ) {
			result[i] = dicts[i].getDictValue3();
		}
		return StringUtils.join( result, "," );
	}

	public Map<String, Dictionary> getDictionariesByType( String type ) {
		return getDictionariesByTypeAndParent( type, null );
	}

	public Map<String, Dictionary> getDictionariesByTypeAndParent(	String type,
																	String parentKey ) {

		Map<String, Dictionary> result = new HashMap<String, Dictionary>();
		if ( StringUtils.isBlank( parentKey ) ) result = dictionaries.get( type );

		else {
			Map<String, Dictionary> tmpResult = dictionaries.get( type );
			Set<Map.Entry<String, Dictionary>> entrys = tmpResult.entrySet();
			for ( Map.Entry<String, Dictionary> entry : entrys ) {
				if ( parentKey.equals( entry.getValue().getParentKey() ) ) result.put( entry.getKey(), entry.getValue() );
			}
		}

		return result;
	}

	public List<Dictionary> getDictionaryListByType( String type ) {
		return getDictionaryListByTypeAndParent( type, null );
	}

	public List<Dictionary> getDictionaryListByTypeAndParent(	String type,
																String parentKey ) {

		Map<String, Dictionary> result = new HashMap<String, Dictionary>();
		if ( StringUtils.isBlank( parentKey ) ) result = dictionaries.get( type );

		else {
			Map<String, Dictionary> tmpResult = dictionaries.get( type );
			Set<Map.Entry<String, Dictionary>> entrys = tmpResult.entrySet();
			for ( Map.Entry<String, Dictionary> entry : entrys ) {
				if ( parentKey.equals( entry.getValue().getParentKey() ) ) result.put( entry.getKey(), entry.getValue() );
			}
		}
		
		List<Dictionary> resultList = new ArrayList<Dictionary>( result.values() );
		Collections.sort( resultList, new Comparator<Dictionary>() {

			@Override
			public int compare( Dictionary o1, Dictionary o2 ) {
				return o1.getRecordId().compareTo( o2.getRecordId() );
			}
			
		});
		
		return resultList;
	}

	public List<Map<String, Object>> decodeBeansToMaps( List<?> beans ) {

		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

		for ( Object bean : beans ) {
			Map<String, Object> entry = decodeBeanToMap( bean );
			result.add( entry );
		}

		return result;
	}

	public Map<String, Object> decodeBeanToMap( Object bean ) {
		return decodeBeanToMap( bean, null );
	}

	public Map<String, Object> decodeBeanToMap( Object bean,
												String ownerFieldName ) {
		Map<String, Object> result = new HashMap<String, Object>();
		Field[] fields = bean.getClass().getDeclaredFields();
		for ( Field field : fields ) {

			if ( excludeFieldName.contains( field.getName() ) ) continue;

			DictValue dictValue = field.getAnnotation( DictValue.class );
			field.setAccessible( true );

			Object fieldValue = null;

			try {
				fieldValue = field.get( bean );
			}
			catch ( Exception e ) {}

			String fieldName = StringUtils.EMPTY;
			if ( !StringUtils.isBlank( ownerFieldName ) ) {
				fieldName = String.format( "%s#%s", ownerFieldName, field.getName() );
			}
			else {
				fieldName = field.getName();
			}

			if ( fieldValue != null ) {

				if ( !fieldValue.getClass().getName().startsWith( "java.lang" ) ) {

					Map<String, Object> tmpFiledValue = decodeBeanToMap( fieldValue, fieldName );
					result.putAll( tmpFiledValue );

					// tmpFiledValue = decodeBeanToMap(fieldValue, null);
					// result.put( fieldName, tmpFiledValue );

				}
				else {

					if ( dictValue != null ) {
						String decodeValue = StringUtils.EMPTY;
						if ( StringUtils.isBlank( dictValue.value() ) || StringUtils.equalsIgnoreCase( dictValue.value(), "value0" ) ) {
							decodeValue = lookupDictValue0( dictValue.type(), fieldValue.toString() );
						}
						if ( StringUtils.equalsIgnoreCase( dictValue.value(), "value1" ) ) {
							decodeValue = lookupDictValue1( dictValue.type(), fieldValue.toString() );
						}
						if ( StringUtils.equalsIgnoreCase( dictValue.value(), "value2" ) ) {
							decodeValue = lookupDictValue2( dictValue.type(), fieldValue.toString() );
						}
						if ( StringUtils.equalsIgnoreCase( dictValue.value(), "value3" ) ) {
							decodeValue = lookupDictValue3( dictValue.type(), fieldValue.toString() );
						}
						result.put( String.format( "%s_dv", fieldName ), decodeValue );
					}

					result.put( fieldName, fieldValue );
				}
			}

			else {
				result.put( fieldName, StringUtils.EMPTY );
			}

		}
		return result;
	}

	public <T> Page<Map<String, Object>> decodePage( Page<T> page ) {

		List<Map<String, Object>> content = decodeBeansToMaps( page.getContent() );

		Page<Map<String, Object>> result = new Page<Map<String, Object>>( content, page.getPageRequest(), page.getTotalCount() );
		result.setContent( content );

		return result;

	}

	static class A {

		private String a;
		private B b;

		public String getA() {
			return a;
		}

		public void setA( String a ) {
			this.a = a;
		}

		public B getB() {
			return b;
		}

		public void setB( B b ) {
			this.b = b;
		}

	}

	static class B {

		private String b;
		private C c;

		public String getB() {
			return b;
		}

		public void setB( String b ) {
			this.b = b;
		}

		public C getC() {
			return c;
		}

		public void setC( C c ) {
			this.c = c;
		}

	}

	static class C {

		private String c;

		public String getC() {
			return c;
		}

		public void setC( String c ) {
			this.c = c;
		}

	}

	public static void main( String[] args ) {
		A a = new A();
		B b = new B();
		C c = new C();

		c.setC( "c" );
		b.setB( "b" );
		b.setC( c );
		a.setA( "a" );
		a.setB( b );

		DictionaryHelper dictionaryHelper = new DictionaryHelper();
		Map<String, Object> result = dictionaryHelper.decodeBeanToMap( a );
		System.out.println( result );

	}
}
